#pragma once
#include <cstddef>
#include <new.h>
#include "TypeTrait.h"
namespace mini
{
	template<typename T >
	inline T* _Allocate(ptrdiff_t size, T*)
	{
		set_new_handler(0);
		T* pTemp = (T*)::operator new((size_t)(size * sizeof(T)));
		if (nullptr == pTemp)
		{
			exit(1);
		}
		return pTemp;
	}

	template< typename T>
	inline void _Deallocate(T* pBuffer)
	{
		::operator delete(pBuffer);
	}

	template<typename T1, typename T2>
	inline void _Construct(T1* p, const T2& value)
	{
		if (nullptr != p)
		{
			new(p)T1(value);
		}
	}

	template<typename T>
	inline void _Destroy(T* pObject)
	{
		if (nullptr != pObject)
		{
			pObject->~T();
		}
	}
	
	template<typename ForwardIterator>
	inline void __destroy_aux(ForwardIterator first, ForwardIterator last, __true_type)
	{

	}
	
	template<typename ForwardIterator>
	inline void __destroy_aux(ForwardIterator first, ForwardIterator last, __false_type)
	{
		for (; first <last; ++first)
		{
			_Destroy(&*first);
		}
	}

	template<typename ForwardIterator, typename T>
	inline void __destroy(ForwardIterator first, ForwardIterator last, T)
	{
		typedef typename  __type_trait<T>::has_destructor trivial_destructor;
		__destroy_aux(first, last, trivial_destructor());
	}

	template<typename ForwardIterator>
	inline void _Destroy(ForwardIterator first, ForwardIterator last )
	{
		__destroy(first, last, value_type(first));
	}


	class CDefault_Allocator
	{
		enum
		{
			__ALIGN = 8
		};
		enum
		{
			__MAX_BYTES = 128
		};
		enum
		{
			__NFREELISTS = __MAX_BYTES / __ALIGN
		};
		union object
		{
			object* pFreeListLink = nullptr;
			char clientData[1];
		};

	private:
		static size_t RoundUp(size_t bytes)
		{
			return (bytes + __ALIGN - 1) & ~(__ALIGN - 1);
		}
		static size_t freeListIndex(size_t bytes)
		{
			return (bytes + __ALIGN - 1) / __ALIGN - 1;
		}
		static void* refill(size_t size)
		{
			int nObjectCount = 4;
			byte* pChunk = chunkAllocate(size, nObjectCount);
			object** myFreeLists = nullptr;
			object* pResult = nullptr;
			object* pCurrentObject = nullptr;
			object* pNextObject = nullptr;
			if (nObjectCount > 1)
			{
				myFreeLists = freeLists + freeListIndex(size);
				pResult = (object*)pChunk;

				pCurrentObject = (object*)(pChunk + size);
				*myFreeLists = pCurrentObject;
				for (int i = 2; i < nObjectCount; ++i)
				{
					pCurrentObject->pFreeListLink = (object*)(pChunk + i * size);
					pCurrentObject = pCurrentObject->pFreeListLink;
				}
				pCurrentObject->pFreeListLink = nullptr;
				return pResult;
			}
			else
			{
				return pChunk;
			}
		}

		static byte* chunkAllocate(size_t size, int &nObjects)
		{
			byte* pResult = nullptr;
			size_t totalBytes = size * nObjects;
			size_t memory_left = endFree - startFree;
			if (memory_left >= totalBytes)
			{
				pResult = startFree;
				startFree += totalBytes;
				return pResult;
			}
			else if (memory_left >= size)
			{
				nObjects = memory_left / size;
				totalBytes = size * nObjects;
				pResult = startFree;
				startFree += totalBytes;
				return pResult;
			}
			else
			{
				size_t bytesToGet = 2 * totalBytes + RoundUp(heapSize >> 4);
				if (memory_left > 0)
				{
					object** myFreeList = freeLists + freeListIndex(memory_left);
					((object*)startFree)->pFreeListLink = *myFreeList;
					*myFreeList = (object*)startFree;
				}
				startFree = (byte*)malloc(bytesToGet);
				if (nullptr == startFree)
				{
					object** myFreeList = nullptr;
					object* pObject = nullptr;
					for (int i = size; i < __MAX_BYTES; i += __ALIGN)
					{
						myFreeList = freeLists + freeListIndex(i);
						pObject = *myFreeList;
						if (nullptr != pObject)
						{
							*myFreeList = pObject->pFreeListLink;
							startFree = (byte*)pObject;
							endFree = startFree + i;
							return chunkAllocate(size, nObjects);
						}
					}
					endFree = nullptr;
					startFree = (byte*)malloc(bytesToGet);
				}
				heapSize += bytesToGet;
				endFree = startFree + bytesToGet;
				return chunkAllocate(size, nObjects);
			}
		}

	public:
		static void* allocate(size_t n)
		{
			if (n > (size_t)__MAX_BYTES)
			{
				return malloc(n);
			}
			else
			{
				object** myFreeList = freeLists + freeListIndex(n);
				object* pResult = *myFreeList;
				if (nullptr != pResult)
				{
					*myFreeList = pResult->pFreeListLink;
					return pResult;
				}
				else
				{
					return refill(RoundUp(n));
				}
			}
		}

		static void deallocate(void* pBuffer, size_t n)
		{
			if (n > (size_t)__MAX_BYTES)
			{
				return free(pBuffer);
			}
			else
			{
				object** myFreeList = freeLists + freeListIndex(n);
				object* pResult = *myFreeList;
				object* pObject = static_cast<object*>(pBuffer);
				pObject->pFreeListLink = *myFreeList;
				*myFreeList = pObject;
			}
		}

		static void* reallocate(void* pBuffer, size_t oldSize, size_t newSize)
		{
			return nullptr;
		}
	private:
		static object*  freeLists[__NFREELISTS];
		static byte*	startFree;
		static byte*	endFree;
		static size_t	heapSize;
	};


	template<typename T>
	class TAllocator
	{
	public:
		typedef T value_type;
		typedef T* pointer;
		typedef const T* const_pointer;
		typedef T&	reference;
		typedef const T& const_reference;
		typedef size_t size_type;
		typedef ptrdiff_t difference_type;

		template<typename U>
		struct rebind
		{
			typedef TAllocator<U> other;
		};

	public:
		TAllocator()
		{

		}
		~TAllocator()
		{
		}
		static pointer allocate(size_type n, const void* hint = nullptr)
		{
			return _Allocate((difference_type)n, (pointer)0);
		}
		static void deallocate(pointer p, size_type n)
		{
			_Deallocate(p);
		}

		static void construct(pointer p, const T& value)
		{
			_Construct(p, value);
		}

		static void destory(pointer p)
		{
			_Destroy(p);
		}
 
		static size_type max_size()
		{
			return size_type(UINT_MAX / sizeof(T));
		}
		static pointer address(reference x)
		{
			return (pointer)&x;
		}
		static const_pointer const_address(const_reference x)
		{
			return (const_pointer)&x;
		}
	};

}